#include "trifractal.h"
#include <cmath>

/*==============================================================================
                              Trifractal effect class
==============================================================================*/

TriFractal::TriFractal(Video* video, Sound* sound)
: Effect(video, sound), background_last_update(0.0f)
{
}

TriFractal::~TriFractal()
{
}

// Draw effect on screen
void TriFractal::Render()
{
	RenderBackground();

	float t = GetTime();

	static float damped_amplitude1 = 0.0f;
	damped_amplitude1 = Lerp(damped_amplitude1, GetSoundDB() * 3.0f, 0.15f);

	static float damped_amplitude2 = 0.0f;
	damped_amplitude2 = Lerp(damped_amplitude2, GetSoundDB() * 12.0f, 0.4f);

	static float damped_freq = 0.0f;
	damped_freq = Lerp(damped_freq, GetSoundFreq(), 0.1f);

	Vector2 c(sin(t / 6.2f) * 0.8f, cos(t / 12.0f) * 0.8f);
	DrawFractal(c, 0.3f, 0.55f + damped_freq / (MAXFREQ * 5.0f), 0.09f, 0.76f, sin(t / 3.1f), 0.1f + damped_amplitude1, 6);
}

// Draws a triangle
void TriFractal::DrawEqlTri(const EqlTri& tri)
{
	const float a = tri.phi;
	const float b = a + PI * 2.0f / 3.0f;
	const float c = a - PI * 2.0f / 3.0f;

	const Vector2 p1(tri.c.x + cos(a) * tri.r, tri.c.y + sin(a) * tri.r);
	const Vector2 p2(tri.c.x + cos(b) * tri.r, tri.c.y + sin(b) * tri.r);
	const Vector2 p3(tri.c.x + cos(c) * tri.r, tri.c.y + sin(c) * tri.r);

	video->DrawTriangle(p1, p2, p3, tri.color);
}

// Draw background
void TriFractal::RenderBackground()
{
	// Update background
	if(GetTime() > background_last_update + background_update_interval)
	{
		background_last_update = GetTime();

		float norm_freq = UnLerp(50.0f, MAXFREQ, GetSoundFreq());
		float min_r = norm_freq * 0.1f;
		float max_r = norm_freq * 0.3f;

		for(int i = 0; i < background_tris; ++i)
		{
			int color = RandomInt(64, 196);
			background[i].c = Vector2(RandomFloat(-1.0f, 1.0f), RandomFloat(-1.0f, 1.0f));
			background[i].r = Lerp(min_r, max_r, RandomFloat());
			background[i].phi = Lerp(0.0, 2.0f * PI, RandomFloat());
			background[i].color = Color(color, color, color, 8);
		}
	}

	// White background-background
	video->DrawBackground(COLOR_WHITE);

	// Real background
	for(int i = 0; i < background_tris; ++i)
		DrawEqlTri(background[i]);		
}

// Draw fractal - main part of the effect
void TriFractal::DrawFractal(const Vector2& pos, float distance, float distance_ratio, 
        float size, float size_ratio, float rotation, float delta_rotation, int depth)
{
        if(depth == 0)
                return;

        Vector2 vec1(0.0f, -1.0f);
        Vector2 vec2 = vec1.GetRotated(rotation + PI * 2.0f / 3.0f);
        Vector2 vec3 = vec1.GetRotated(rotation - PI * 2.0f / 3.0f);
        vec1.Rotate(rotation);

        video->DrawTriangle(pos + (vec1 * size), pos + (vec2 * size), pos + (vec3 * size), Color(0, 0, 0, 255));

        vec1 = pos + (vec1 * distance);
        vec2 = pos + (vec2 * distance);
        vec3 = pos + (vec3 * distance);

		// Using recursion
        DrawFractal(vec1, distance * distance_ratio, distance_ratio, size * size_ratio, size_ratio, 
                rotation + delta_rotation, delta_rotation, depth - 1);
        DrawFractal(vec2, distance * distance_ratio, distance_ratio, size * size_ratio, size_ratio, 
                rotation + delta_rotation, delta_rotation, depth - 1);
        DrawFractal(vec3, distance * distance_ratio, distance_ratio, size * size_ratio, size_ratio, 
                rotation + delta_rotation, delta_rotation, depth - 1);
}